﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    internal class TableFeatureType
    {
        private XLUnicodeString _cSpName;
        private uint _cbFsData = 64;
        private uint _cbStmCache;
        private uint _cchStmCache;
        private Feat11RgInvalidCells _cellInvalid;
        private bool _crwHeader;
        private XLUnicodeString _entryId;
        private bool _fApplyAutoFilter = true;
        private bool _fAutoFilter = true;
        private bool _fCompressedXml;
        private bool _fForceInsertToBeVis;
        private bool _fGoodRupBld = true;
        private bool _fInsertRowInsCells;
        private bool _fLoadCspName;
        private bool _fLoadEntryId;
        private bool _fLoadPldwIdChanged;
        private bool _fLoadPldwIdDeleted;
        private bool _fLoadPllstclInvalid;
        private bool _fNeedsCommit;
        private bool _fPersistAutoFilter = true;
        private bool _fPublished;
        private bool _fShowInsertRow;
        private bool _fShownTotalRow;
        private bool _fSingleCell;
        private List<Feat11FieldDataItem> _fieldData;
        private Feat11RgSharepointIdChange _idChanged;
        private Feat11RgSharepointIdDel _idDeleted;
        private uint _idFieldNext;
        private uint _idList;
        private uint _lPosStmCache;
        private LEMMode _lem;
        private SourceType _lt;
        private bool _reserved1;
        private bool _reserved2;
        private byte[] _rgbHashParam = new byte[16];
        private XLUnicodeString _rgbName;
        private ushort _rupBuild;
        private ushort _unused1;
        private bool _unused2;
        private bool _unused3 = true;
        private byte _verXL = 0xe;

        public TableFeatureType(Table table, int id)
        {
            Contract.Requires(table != null);
            Debug.WriteLine("todo");
            _fieldData = new List<Feat11FieldDataItem>();
            uint uid = 1;
            foreach (var column in table.Columns)
            {
                _fieldData.Add(new Feat11FieldDataItem(column, uid++));
            }
            _idFieldNext = uid;
            _idList = (uint) id;
            _crwHeader = true;
            _entryId = (XLUnicodeString) id.ToString();
            _fLoadEntryId = true;
            _rgbName = (XLUnicodeString) table.Name;
            _rupBuild = 12902;

        }

        public TableFeatureType()
        {
        }

        
        protected ushort Qu
        {
            get
            {
                            Contract.Assert(_verXL == 0xB || _verXL == 0xC || _verXL == 0xe);
            ushort result = _verXL;
                if (_fLoadEntryId) result |= 0x0010;
                if (_fLoadPllstclInvalid) result |= 0x0020;
                if (_fGoodRupBld) result |= 0x0040;
                if (_unused3) result |= 0x0080;
                if (_fPublished) result |= 0x00100;
                return result;
            }
        }

        protected ushort Ap
        {
            get
            {
                ushort result = 0;
                if (_unused2) result |= 0x0001;
                if (_fAutoFilter) result |= 0x0002;
                if (_fPersistAutoFilter) result |= 0x0004;
                if (_fShowInsertRow) result |= 0x0008;
                if (_fInsertRowInsCells) result |= 0x0010;
                if (_fLoadPldwIdDeleted) result |= 0x0020;
                if (_fShownTotalRow) result |= 0x0040;
                if (_reserved1) result |= 0x0080;
                if (_fNeedsCommit) result |= 0x0100;
                if (_fSingleCell) result |= 0x0200;
                if (_reserved2) result |= 0x0400;
                if (_fApplyAutoFilter) result |= 0x0800;
                if (_fForceInsertToBeVis) result |= 0x1000;
                if (_fCompressedXml) result |= 0x2000;
                if (_fLoadCspName) result |= 0x4000;
                if (_fLoadPldwIdChanged) result |= 0x800;
                return result;
            }
        }

        public List<Feat11FieldDataItem> FieldData
        {
            get { return _fieldData; }
        }

        public bool CrwTotals { get; private set; }

        public void Read(BinaryReader reader)
        {
#if DEBUG
            var pos = reader.BaseStream.Position;
#endif
            _lt = (SourceType) reader.ReadInt32();
            _idList = reader.ReadUInt32();
            _crwHeader = reader.ReadInt32() != 0;
            CrwTotals = reader.ReadInt32() != 0;
            _idFieldNext = reader.ReadUInt32();
            _cbFsData = reader.ReadUInt32();
            Contract.Assert(_cbFsData == 64);
            _rupBuild = reader.ReadUInt16();
            _unused1 = reader.ReadUInt16();
            ushort ap = reader.ReadUInt16();
            _unused2 = (ap & 0x0001) != 0;
            _fAutoFilter = (ap & 0x0002) != 0;
            _fPersistAutoFilter = (ap & 0x004) != 0;
            _fShowInsertRow = (ap & 0x008) != 0;
            _fInsertRowInsCells = (ap & 0x010) != 0;
            _fLoadPldwIdDeleted = (ap & 0x020) != 0;
            _fShownTotalRow = (ap & 0x0040) != 0;
            _reserved1 = (ap & 0x0080) != 0;
            _fNeedsCommit = (ap & 0x0100) != 0;
            _fSingleCell = (ap & 0x200) != 0;
            _reserved2 = (ap & 0x400) != 0;
            _fApplyAutoFilter = (ap & 0x800) != 0;
            _fForceInsertToBeVis = (ap & 0x1000) != 0;
            _fCompressedXml = (ap & 0x2000) != 0;
            _fLoadCspName = (ap & 0x4000) != 0;
            _fLoadPldwIdChanged = (ap & 0x8000) != 0;
            Contract.Assert(Ap == ap);
            ushort qu = reader.ReadUInt16();
            _verXL = (byte) (qu & 0xF);
            Contract.Assert(_verXL == 0xB || _verXL == 0xC || _verXL == 0xe);
            _fLoadEntryId = (qu & 0x10) != 0;
            _fLoadPllstclInvalid = (qu & 0x20) != 0;
            _fGoodRupBld = (qu & 0x40) != 0;
            _unused3 = (qu & 0x80) != 0;
            _fPublished = (qu & 0x100) != 0;
            Contract.Assert(Qu == qu);
            _lPosStmCache = reader.ReadUInt32();
            _cbStmCache = reader.ReadUInt32();
            _cchStmCache = reader.ReadUInt32();
            _lem = (LEMMode) reader.ReadInt32();
            _rgbHashParam = reader.ReadBytes(16);
            _rgbName = new XLUnicodeString();
            _rgbName.Read(reader);
            ushort cFieldData = reader.ReadUInt16();
            if (_fLoadCspName)
            {
                _cSpName = new XLUnicodeString();
                _cSpName.Read(reader);
            }
            if (_fLoadEntryId)
            {
                _entryId = new XLUnicodeString();
                _entryId.Read(reader);
            }
            _fieldData = new List<Feat11FieldDataItem>();
            for (int i = 0; i < cFieldData; i++)
            {
                var item = new Feat11FieldDataItem();
                item.Read(reader, _lt, _crwHeader, _fSingleCell, this._lt);
                FieldData.Add(item);
            }
            if (_fLoadPldwIdDeleted)
            {
                _idDeleted = new Feat11RgSharepointIdDel();
                _idDeleted.Read(reader);
            }
            if (_fLoadPldwIdChanged)
            {
                _idChanged = new Feat11RgSharepointIdChange();
                _idChanged.Read(reader);
            }
            if (_fLoadPllstclInvalid)
            {
                _cellInvalid = new Feat11RgInvalidCells();
                _cellInvalid.Read(reader);
            }
        }

        public void Write(BinaryWriter writer)
        {
            writer.Write((int) _lt);
            writer.Write(_idList );
            writer.Write(_crwHeader?1:0) ;
            writer.Write(CrwTotals?1:0) ;
            writer.Write(_idFieldNext);
            writer.Write(_cbFsData);
            Contract.Assert(_cbFsData == 64);
             writer.Write(_rupBuild);
            writer.Write(_unused1);
            writer.Write(Ap);
            writer.Write(Qu);
             writer.Write(_lPosStmCache);
            writer.Write(_cbStmCache );
            writer.Write(_cchStmCache );
             writer.Write((int)_lem);
             Contract.Assert(_rgbHashParam != null);
            writer.Write(_rgbHashParam);
            _rgbName.Write(writer);
            ushort cFieldData = (ushort) _fieldData.Count;
            writer.Write(cFieldData);
            if (_fLoadCspName)
            {
                _cSpName.Write(writer);
            }
            if (_fLoadEntryId)
            {
                _entryId.Write(writer);
            }
            foreach (var item in _fieldData)
                item.Write(writer);
            if (_fLoadPldwIdDeleted)
            {
                _idDeleted.Write(writer);
            }
            if (_fLoadPldwIdChanged)
            {
                _idChanged.Write(writer);
            }
            if (_fLoadPllstclInvalid)
            {
                _cellInvalid.Write(writer);
            }
        }
    }
}